/*-------------------------------------------------------------------------
 *
 * pg_depend.c
 *	  支持pg_depend关系操作的常规例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/pg_depend.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_extension.h"
#include "commands/extension.h"
#include "miscadmin.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"


static bool fc_isObjectPinned(const ObjectAddress *fc_object);


/*
 * 通过各自的objectAddress记录两个对象之间的依赖关系。
 * 第一个参数是依赖对象，第二个是它所引用的对象。
 *
 * 这只是创建一个pg_depend中的条目，而没有其他处理。
 */
void recordDependencyOn(const ObjectAddress *fc_depender,
				   const ObjectAddress *fc_referenced,
				   DependencyType fc_behavior)
{
	recordMultipleDependencies(fc_depender, fc_referenced, 1, fc_behavior);
}

/*
 * 为单个依赖对象记录多个依赖项（相同类型）。这比分别记录每一个略少开销。
 */
void recordMultipleDependencies(const ObjectAddress *fc_depender,
						   const ObjectAddress *fc_referenced,
						   int fc_nreferenced,
						   DependencyType fc_behavior)
{
	Relation	fc_dependDesc;
	CatalogIndexState fc_indstate;
	TupleTableSlot **fc_slot;
	int			fc_i,
				fc_max_slots,
				fc_slot_init_count,
				fc_slot_stored_count;

	if (fc_nreferenced <= 0)
		return;					/* 无需操作 */

	/*
	 * 在引导过程中，不做任何操作，因为pg_depend可能尚不存在。
	 *
	 * 引导过程中创建的对象很可能是固定的，而那些不是固定的对象之间也没有依赖关系，因此无论如何不需要创建pg_depend条目。
	 */
	if (IsBootstrapProcessingMode())
		return;

	fc_dependDesc = table_open(DependRelationId, RowExclusiveLock);

	/*
	 * 分配要使用的槽，但在知道它们将被使用之前，延迟高成本的初始化。
	 */
	fc_max_slots = Min(fc_nreferenced,
					MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
	fc_slot = palloc(sizeof(TupleTableSlot *) * fc_max_slots);

	/* 除非需要进行更新，否则不打开索引 */
	fc_indstate = NULL;

	/* 当前存储元组的槽数 */
	fc_slot_stored_count = 0;
	/* 当前初始化的槽数 */
	fc_slot_init_count = 0;
	for (fc_i = 0; fc_i < fc_nreferenced; fc_i++, fc_referenced++)
	{
		/*
		 * 如果引用的对象被系统固定，则没有真正的需求记录对它的依赖关系。这节省了pg_depend中的大量空间，因此值得花时间检查。
		 */
		if (fc_isObjectPinned(fc_referenced))
			continue;

		if (fc_slot_init_count < fc_max_slots)
		{
			fc_slot[fc_slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(fc_dependDesc),
															   &TTSOpsHeapTuple);
			fc_slot_init_count++;
		}

		ExecClearTuple(fc_slot[fc_slot_stored_count]);

		/*
		 * 记录依赖关系。注意，我们不在意检查重复的依赖关系；它们不会造成任何伤害。
		 */
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(fc_referenced->classId);
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(fc_referenced->objectId);
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(fc_referenced->objectSubId);
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) fc_behavior);
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(fc_depender->classId);
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(fc_depender->objectId);
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(fc_depender->objectSubId);

		memset(fc_slot[fc_slot_stored_count]->tts_isnull, false,
			   fc_slot[fc_slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));

		ExecStoreVirtualTuple(fc_slot[fc_slot_stored_count]);
		fc_slot_stored_count++;

		/* 如果槽已满，插入一批元组 */
		if (fc_slot_stored_count == fc_max_slots)
		{
			/* 仅在我们知道需要时获取索引信息 */
			if (fc_indstate == NULL)
				fc_indstate = CatalogOpenIndexes(fc_dependDesc);

			CatalogTuplesMultiInsertWithInfo(fc_dependDesc, fc_slot, fc_slot_stored_count,
											 fc_indstate);
			fc_slot_stored_count = 0;
		}
	}

	/* 插入缓冲区中剩余的任何元组 */
	if (fc_slot_stored_count > 0)
	{
		/* 仅在我们知道需要时获取索引信息 */
		if (fc_indstate == NULL)
			fc_indstate = CatalogOpenIndexes(fc_dependDesc);

		CatalogTuplesMultiInsertWithInfo(fc_dependDesc, fc_slot, fc_slot_stored_count,
										 fc_indstate);
	}

	if (fc_indstate != NULL)
		CatalogCloseIndexes(fc_indstate);

	table_close(fc_dependDesc, RowExclusiveLock);

	/* 仅删除使用的槽数 */
	for (fc_i = 0; fc_i < fc_slot_init_count; fc_i++)
		ExecDropSingleTupleTableSlot(fc_slot[fc_i]);
	pfree(fc_slot);
}

/*
 * 如果我们正在执行CREATE EXTENSION操作，则将给定对象标记为扩展的成员，或检查它是否已经是。
 * 否则，不做任何操作。
 *
 * 这必须在创建任何用户可定义对象类型时调用，该类型可能是扩展的成员。
 *
 * 如果对象已经存在，则isReplace必须为true，如果是新创建的，则为false。
 * 在前一种情况下，我们坚持要求它已经是当前扩展的成员。
 * 在后一种情况下，我们可以跳过检查它是否已经是任何扩展的成员。
 *
 * 注意：在CREATE OR REPLACE及类似命令中，isReplace = true通常用于更新对象。
 * 我们曾经允许目标对象不是扩展成员，而是默默地将其纳入当前扩展中。
 * 然而，这既容易出错（扩展可能无意中覆盖独立对象），又是安全隐患（因为该对象将保留其先前的所有权）。
 */
void recordDependencyOnCurrentExtension(const ObjectAddress *fc_object,
								   bool fc_isReplace)
{
	/* 只有完整对象可以成为扩展成员 */
	Assert(fc_object->objectSubId == 0);

	if (creating_extension)
	{
		ObjectAddress fc_extension;

		/* 只有在isReplace时才需要检查现有成员资格 */
		if (fc_isReplace)
		{
			Oid			fc_oldext;

			/*
			 * 附带说明：这些目录查找之所以安全，是因为对象是预先存在的。
			 * 在非isReplace情况下，调用者很可能尚未执行使新对象可见的CommandCounterIncrement。
			 */
			fc_oldext = getExtensionOfObject(fc_object->classId, fc_object->objectId);
			if (OidIsValid(fc_oldext))
			{
				/* 如果已经是此扩展的成员，则无事可做 */
				if (fc_oldext == CurrentExtensionObject)
					return;
				/* 已经是其他扩展的成员，因此拒绝 */
				ereport(ERROR,
						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
						 errmsg("%s is already a member of extension \"%s\"",
								getObjectDescription(fc_object, false),
								get_extension_name(fc_oldext))));
			}
			/* 这是一个独立对象，因此拒绝 */
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("%s is not a member of extension \"%s\"",
							getObjectDescription(fc_object, false),
							get_extension_name(CurrentExtensionObject)),
					 errdetail("An extension is not allowed to replace an object that it does not own.")));
		}

		/* 好的，将其记录为CurrentExtensionObject的成员 */
		fc_extension.classId = ExtensionRelationId;
		fc_extension.objectId = CurrentExtensionObject;
		fc_extension.objectSubId = 0;

		recordDependencyOn(fc_object, &fc_extension, DEPENDENCY_EXTENSION);
	}
}

/*
 * 如果我们正在执行CREATE EXTENSION操作，检查给定对象是否是扩展的成员，如果不是，则抛出错误。
 * 否则，不做任何操作。
 *
 * 每当CREATE IF NOT EXISTS操作（对于可以是扩展成员的对象类型）发现所需名称的对象已经存在时，必须调用它。
 * 除非冲突对象已经是扩展成员，否则扩展使用IF NOT EXISTS是没有安全性的；否则，恶意用户可以替代一个具有任意属性的对象。
 */
void checkMembershipInCurrentExtension(const ObjectAddress *fc_object)
{
	/*
	 * 这实际上是recordDependencyOnCurrentExtension中测试的相同条件；
	 * 但我们想要发出不同措辞的错误，无论如何，在这种情况下调用recordDependencyOnCurrentExtension会让人感到困惑。
	 */

	/* 只有完整对象可以成为扩展成员 */
	Assert(fc_object->objectSubId == 0);

	if (creating_extension)
	{
		Oid			fc_oldext;

		fc_oldext = getExtensionOfObject(fc_object->classId, fc_object->objectId);
		
/******************************************************************************* 如果已经是该扩展的成员，没问题 */
		if (fc_oldext == CurrentExtensionObject)
			return;
		/* 否则进行投诉 */
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("%s is not a member of extension \"%s\"",
						getObjectDescription(fc_object, false),
						get_extension_name(CurrentExtensionObject)),
				 errdetail("An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.")));
	}
}

/*
 * deleteDependencyRecordsFor -- 删除所有具有给定依赖者的记录
 * classId/objectId。返回删除的记录数。
 *
 * 当重新定义一个现有对象时使用。指向该对象的链接不会改变，
 * 而从该对象指向的链接将被重新创建
 * （可能与之前有一些不同）。
 *
 * 如果skipExtensionDeps为真，我们不删除任何显示给定对象是
 * 扩展成员的依赖关系。这避免了需要很多额外的逻辑来获取和
 * 重新创建该依赖关系。
 */
long deleteDependencyRecordsFor(Oid fc_classId, Oid fc_objectId,
						   bool fc_skipExtensionDeps)
{
	long		fc_count = 0;
	Relation	fc_depRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	fc_depRel = table_open(DependRelationId, RowExclusiveLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_objectId));

	fc_scan = systable_beginscan(fc_depRel, DependDependerIndexId, true,
							  NULL, 2, fc_key);

	while (HeapTupleIsValid(fc_tup = systable_getnext(fc_scan)))
	{
		if (fc_skipExtensionDeps &&
			((Form_pg_depend) GETSTRUCT(fc_tup))->deptype == DEPENDENCY_EXTENSION)
			continue;

		CatalogTupleDelete(fc_depRel, &fc_tup->t_self);
		fc_count++;
	}

	systable_endscan(fc_scan);

	table_close(fc_depRel, RowExclusiveLock);

	return fc_count;
}

/*
 * deleteDependencyRecordsForClass -- 删除所有具有给定依赖者
 * classId/objectId，依赖对象 classId 和 deptype 的记录。
 * 返回删除的记录数量。
 *
 * 这是 deleteDependencyRecordsFor 的一个变体，当撤销
 * 通过依赖记录（例如扩展成员资格）表达的对象属性时很有用。
 */
long deleteDependencyRecordsForClass(Oid fc_classId, Oid fc_objectId,
								Oid fc_refclassId, char fc_deptype)
{
	long		fc_count = 0;
	Relation	fc_depRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	fc_depRel = table_open(DependRelationId, RowExclusiveLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_objectId));

	fc_scan = systable_beginscan(fc_depRel, DependDependerIndexId, true,
							  NULL, 2, fc_key);

	while (HeapTupleIsValid(fc_tup = systable_getnext(fc_scan)))
	{
		Form_pg_depend fc_depform = (Form_pg_depend) GETSTRUCT(fc_tup);

		if (fc_depform->refclassid == fc_refclassId && fc_depform->deptype == fc_deptype)
		{
			CatalogTupleDelete(fc_depRel, &fc_tup->t_self);
			fc_count++;
		}
	}

	systable_endscan(fc_scan);

	table_close(fc_depRel, RowExclusiveLock);

	return fc_count;
}

/*
 * deleteDependencyRecordsForSpecific -- 删除所有具有给定依赖者
 * classId/objectId，依赖对象 classId/objectId 和给定 deptype 的记录。
 * 返回删除的记录数量。
 */
long deleteDependencyRecordsForSpecific(Oid fc_classId, Oid fc_objectId, char fc_deptype,
								   Oid fc_refclassId, Oid fc_refobjectId)
{
	long		fc_count = 0;
	Relation	fc_depRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	fc_depRel = table_open(DependRelationId, RowExclusiveLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_objectId));

	fc_scan = systable_beginscan(fc_depRel, DependDependerIndexId, true,
							  NULL, 2, fc_key);

	while (HeapTupleIsValid(fc_tup = systable_getnext(fc_scan)))
	{
		Form_pg_depend fc_depform = (Form_pg_depend) GETSTRUCT(fc_tup);

		if (fc_depform->refclassid == fc_refclassId &&
			fc_depform->refobjid == fc_refobjectId &&
			fc_depform->deptype == fc_deptype)
		{
			CatalogTupleDelete(fc_depRel, &fc_tup->t_self);
			fc_count++;
		}
	}

	systable_endscan(fc_scan);

	table_close(fc_depRel, RowExclusiveLock);

	return fc_count;
}

/*
 * 调整依赖记录以指向同类型的不同对象
 *
 * classId/objectId 指定引用对象。
 * refClassId/oldRefObjectId 指定旧的被引用对象。
 * newRefObjectId 是新的被引用对象（必须是类 refClassId 的）。
 *
 * 请注意缺少 objsubid 参数。如果存在子对象引用
 * 它们都将被重新调整。此外，期望我们处理的是 NORMAL 依赖项：如果我们必须
 * 将对固定对象的（隐式）依赖替换为对未固定
 * 对象的显式依赖，则新的依赖将是 NORMAL。
 *
 * 返回更新的记录数量 -- 零表示有问题。
 */
long changeDependencyFor(Oid fc_classId, Oid fc_objectId,
					Oid fc_refClassId, Oid fc_oldRefObjectId,
					Oid fc_newRefObjectId)
{
	long		fc_count = 0;
	Relation	fc_depRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;
	ObjectAddress fc_objAddr;
	ObjectAddress fc_depAddr;
	bool		fc_oldIsPinned;
	bool		fc_newIsPinned;

	/*
	 * 检查 oldRefObjectId 或 newRefObjectId 是否被固定。
	 * 固定的对象不应有任何指向它们的依赖项条目，
	 * 因此在这些情况下，我们应添加或移除 pg_depend 条目，或
	 * 干脆什么也不做，而不是像通常那样更新条目。
	 */
	fc_objAddr.classId = fc_refClassId;
	fc_objAddr.objectId = fc_oldRefObjectId;
	fc_objAddr.objectSubId = 0;

	fc_oldIsPinned = fc_isObjectPinned(&fc_objAddr);

	fc_objAddr.objectId = fc_newRefObjectId;

	fc_newIsPinned = fc_isObjectPinned(&fc_objAddr);

	if (fc_oldIsPinned)
	{
		/*
		 * 如果两个都被固定，我们需要什么都不做。然而，返回 1 而不是 0，
		 * 否则调用者会认为这是一个错误情况。
		 */
		if (fc_newIsPinned)
			return 1;

		/*
		 * 没有旧的依赖记录，但我们应该插入一个新的。
		 * 假设想要一个正常的依赖。
		 */
		fc_depAddr.classId = fc_classId;
		fc_depAddr.objectId = fc_objectId;
		fc_depAddr.objectSubId = 0;
		recordDependencyOn(&fc_depAddr, &fc_objAddr, DEPENDENCY_NORMAL);

		return 1;
	}

	fc_depRel = table_open(DependRelationId, RowExclusiveLock);

	/* 应该有现有的依赖记录，因此进行搜索。 */
	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_objectId));

	fc_scan = systable_beginscan(fc_depRel, DependDependerIndexId, true,
							  NULL, 2, fc_key);

	while (HeapTupleIsValid((fc_tup = systable_getnext(fc_scan))))
	{
		Form_pg_depend fc_depform = (Form_pg_depend) GETSTRUCT(fc_tup);

		if (fc_depform->refclassid == fc_refClassId &&
			fc_depform->refobjid == fc_oldRefObjectId)
		{
			if (fc_newIsPinned)
				CatalogTupleDelete(fc_depRel, &fc_tup->t_self);
			else
			{
				/* 制作一个可修改的副本 */
				fc_tup = heap_copytuple(fc_tup);
				fc_depform = (Form_pg_depend) GETSTRUCT(fc_tup);

				fc_depform->refobjid = fc_newRefObjectId;

				CatalogTupleUpdate(fc_depRel, &fc_tup->t_self, fc_tup);

				heap_freetuple(fc_tup);
			}

			fc_count++;
		}
	}

	systable_endscan(fc_scan);

	table_close(fc_depRel, RowExclusiveLock);

	return fc_count;
}

/*
 * 调整所有依赖记录以来自同类型的不同对象
 *
 * classId/oldObjectId 指定旧的引用对象。
 * newObjectId 是新的引用对象（必须是类 classId 的）。
 *
 * 返回更新的记录数量。
 */
long changeDependenciesOf(Oid fc_classId, Oid fc_oldObjectId,
					 Oid fc_newObjectId)
{
	long		fc_count = 0;
	Relation	fc_depRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	fc_depRel = table_open(DependRelationId, RowExclusiveLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_oldObjectId));

	fc_scan = systable_beginscan(fc_depRel, DependDependerIndexId, true,
							  NULL, 2, fc_key);

	while (HeapTupleIsValid((fc_tup = systable_getnext(fc_scan))))
	{
		Form_pg_depend fc_depform;

		/* 制作一个可修改的副本 */
		fc_tup = heap_copytuple(fc_tup);
		fc_depform = (Form_pg_depend) GETSTRUCT(fc_tup);

		fc_depform->objid = fc_newObjectId;

		CatalogTupleUpdate(fc_depRel, &fc_tup->t_self, fc_tup);

		heap_freetuple(fc_tup);

		fc_count++;
	}

	systable_endscan(fc_scan);

	table_close(fc_depRel, RowExclusiveLock);

	return fc_count;
}

/*
 * 调整所有依赖记录以指向同类型的不同对象
 *
 * refClassId/oldRefObjectId 指定旧的被引用对象。
 * newRefObjectId 是新的被引用对象（必须是类 refClassId 的）。
 *
 * 返回更新的记录数量。
 */
long changeDependenciesOn(Oid fc_refClassId, Oid fc_oldRefObjectId,
					 Oid fc_newRefObjectId)
{
	long		fc_count = 0;
	Relation	fc_depRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;
	ObjectAddress fc_objAddr;
	bool		fc_newIsPinned;

	fc_depRel = table_open(DependRelationId, RowExclusiveLock);

	/*
	 * 如果 oldRefObjectId 被固定，则不会有任何依赖项条目指向
	 * 它 —— 在这种情况下我们无法处理。（在当前使用中，这并不值得
	 * 编写代码修复；这只是意味着你不能从 pg_catalog 中重命名东西，
	 * 这可能无论如何都是一个错误的做法。）
	 */
	fc_objAddr.classId = fc_refClassId;
	fc_objAddr.objectId = fc_oldRefObjectId;
	fc_objAddr.objectSubId = 0;

	if (fc_isObjectPinned(&fc_objAddr))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot remove dependency on %s because it is a system object",
						getObjectDescription(&fc_objAddr, false))));

	/*
	 * 不过，我们可以处理对某个固定对象添加依赖的情况，因为
	 * 这只意味着删除依赖条目。
	 */
	fc_objAddr.objectId = fc_newRefObjectId;

	fc_newIsPinned = fc_isObjectPinned(&fc_objAddr);

	/* 现在搜索依赖记录 */
	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_refclassid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_refClassId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_refobjid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_oldRefObjectId));

	fc_scan = systable_beginscan(fc_depRel, DependReferenceIndexId, true,
							  NULL, 2, fc_key);

	while (HeapTupleIsValid((fc_tup = systable_getnext(fc_scan))))
	{
		if (fc_newIsPinned)
			CatalogTupleDelete(fc_depRel, &fc_tup->t_self);
		else
		{
			Form_pg_depend fc_depform;

			/* 制作一个可修改的副本 */
			fc_tup = heap_copytuple(fc_tup);
			fc_depform = (Form_pg_depend) GETSTRUCT(fc_tup);

			fc_depform->refobjid = fc_newRefObjectId;

			CatalogTupleUpdate(fc_depRel, &fc_tup->t_self, fc_tup);

			heap_freetuple(fc_tup);
		}

		fc_count++;
	}

	systable_endscan(fc_scan);

	table_close(fc_depRel, RowExclusiveLock);

	return fc_count;
}

/*
 * isObjectPinned()
 *
 * 测试一个对象是否是基本数据库功能所需的。
 *
 * 传递的 subId（如果有）将被忽略；我们假设只有完整对象
 * 是固定的（这也意味着固定它们的组件）。
 */
static bool fc_isObjectPinned(const ObjectAddress *fc_object)
{
	return IsPinnedObject(fc_object->classId, fc_object->objectId);
}


/*
 * 各种针对 pg_depend 的特殊用途查找和操作。
 */


/*
 * 查找包含指定对象的扩展，如果有的话
 *
 * 返回扩展的 OID，如果对象不属于任何扩展则返回 InvalidOid。
 *
 * 扩展成员资格由对象到扩展的 EXTENSION 依赖关系标记。请注意，如果 pg_depend
 * 包含该对象与多个扩展的链接，则结果将是不确定的……但这
 * 不应该发生。
 */
Oid getExtensionOfObject(Oid fc_classId, Oid fc_objectId)
{
	Oid			fc_result = InvalidOid;
	Relation	fc_depRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	fc_depRel = table_open(DependRelationId, AccessShareLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_objectId));

	fc_scan = systable_beginscan(fc_depRel, DependDependerIndexId, true,
							  NULL, 2, fc_key);

	while (HeapTupleIsValid((fc_tup = systable_getnext(fc_scan))))
	{
		Form_pg_depend fc_depform = (Form_pg_depend) GETSTRUCT(fc_tup);

		if (fc_depform->refclassid == ExtensionRelationId &&
			fc_depform->deptype == DEPENDENCY_EXTENSION)
		{
			fc_result = fc_depform->refobjid;
			break;				/* 无需继续扫描 */
		}
	}

	systable_endscan(fc_scan);

	table_close(fc_depRel, AccessShareLock);

	return fc_result;
}

/*
 * 返回给定对象在 DEPENDENCY_AUTO_EXTENSION 模式下依赖的扩展列表（可能为 NIL）。
 */
List * getAutoExtensionsOfObject(Oid fc_classId, Oid fc_objectId)
{
	List	   *fc_result = NIL;
	Relation	fc_depRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	fc_depRel = table_open(DependRelationId, AccessShareLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_objectId));

	fc_scan = systable_beginscan(fc_depRel, DependDependerIndexId, true,
							  NULL, 2, fc_key);

	while (HeapTupleIsValid((fc_tup = systable_getnext(fc_scan))))
	{
		Form_pg_depend fc_depform = (Form_pg_depend) GETSTRUCT(fc_tup);

		if (fc_depform->refclassid == ExtensionRelationId &&
			fc_depform->deptype == DEPENDENCY_AUTO_EXTENSION)
			fc_result = lappend_oid(fc_result, fc_depform->refobjid);
	}

	systable_endscan(fc_scan);

	table_close(fc_depRel, AccessShareLock);

	return fc_result;
}

/*
 * 检测序列是否被标记为由某一列“拥有”
 *
 * 拥有标记是序列到列的 AUTO 或 INTERNAL 依赖项。
 * 如果找到一个，将拥有列的身份存储到 *tableId 和 *colId 中，并返回 true；否则返回 false。
 *
 * 注意：如果有多个这样的 pg_depend 条目，则会返回一个随机的条目到输出参数中。
 * 不过，这不应该发生。
 */
bool sequenceIsOwned(Oid fc_seqId, char fc_deptype, Oid *fc_tableId, int32 *fc_colId)
{
	bool		fc_ret = false;
	Relation	fc_depRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	fc_depRel = table_open(DependRelationId, AccessShareLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationRelationId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_seqId));

	fc_scan = systable_beginscan(fc_depRel, DependDependerIndexId, true,
							  NULL, 2, fc_key);

	while (HeapTupleIsValid((fc_tup = systable_getnext(fc_scan))))
	{
		Form_pg_depend fc_depform = (Form_pg_depend) GETSTRUCT(fc_tup);

		if (fc_depform->refclassid == RelationRelationId &&
			fc_depform->deptype == fc_deptype)
		{
			*fc_tableId = fc_depform->refobjid;
			*fc_colId = fc_depform->refobjsubid;
			fc_ret = true;
			break;				/* 无需继续扫描 */
		}
	}

	systable_endscan(fc_scan);

	table_close(fc_depRel, AccessShareLock);

	return fc_ret;
}

/*
 * 收集由指定关系拥有的所有序列的 OID 列表，
 * 如果指定列则包括列。如果 deptype 不是零，则仅查找具有
 * 指定依赖类型的序列。
 */
static List * fc_getOwnedSequences_internal(Oid fc_relid, AttrNumber fc_attnum, char fc_deptype)
{
	List	   *fc_result = NIL;
	Relation	fc_depRel;
	ScanKeyData fc_key[3];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	fc_depRel = table_open(DependRelationId, AccessShareLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_refclassid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationRelationId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_refobjid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_relid));
	if (fc_attnum)
		ScanKeyInit(&fc_key[2],
					Anum_pg_depend_refobjsubid,
					BTEqualStrategyNumber, F_INT4EQ,
					Int32GetDatum(fc_attnum));

	fc_scan = systable_beginscan(fc_depRel, DependReferenceIndexId, true,
							  NULL, fc_attnum ? 3 : 2, fc_key);

	while (HeapTupleIsValid(fc_tup = systable_getnext(fc_scan)))
	{
		Form_pg_depend fc_deprec = (Form_pg_depend) GETSTRUCT(fc_tup);

		/*
		 * 我们假设序列对列的任何自动或内部依赖
		 * 一定是我们要找的。（我们需要 relkind 测试，因为
		 * 索引也可以对列具有自动依赖。）
		 */
		if (fc_deprec->classid == RelationRelationId &&
			fc_deprec->objsubid == 0 &&
			fc_deprec->refobjsubid != 0 &&
			(fc_deprec->deptype == DEPENDENCY_AUTO || fc_deprec->deptype == DEPENDENCY_INTERNAL) &&
			get_rel_relkind(fc_deprec->objid) == RELKIND_SEQUENCE)
		{
			if (!fc_deptype || fc_deprec->deptype == fc_deptype)
				fc_result = lappend_oid(fc_result, fc_deprec->objid);
		}
	}

	systable_endscan(fc_scan);

	table_close(fc_depRel, AccessShareLock);

	return fc_result;
}

/*
 * 收集由指定关系拥有（身份或序列）的所有序列的 OID 列表。
 */
List * getOwnedSequences(Oid fc_relid)
{
	return fc_getOwnedSequences_internal(fc_relid, 0, 0);
}


/***************************************************************************************
 * 获取拥有的身份序列，如果不正好一个则返回错误。
 ***************************************************************************************/
Oid getIdentitySequence(Oid fc_relid, AttrNumber fc_attnum, bool fc_missing_ok)
{
	List	   *fc_seqlist = fc_getOwnedSequences_internal(fc_relid, fc_attnum, DEPENDENCY_INTERNAL);

	if (list_length(fc_seqlist) > 1)
		elog(ERROR, "more than one owned sequence found");
	else if (list_length(fc_seqlist) < 1)
	{
		if (fc_missing_ok)
			return InvalidOid;
		else
			elog(ERROR, "no owned sequence found");
	}

	return linitial_oid(fc_seqlist);
}

/*
 * get_index_constraint
 *		给定索引的OID，返回其拥有的唯一、主键或排除约束的OID，
 *		如果没有拥有的约束则返回InvalidOid。
 */
Oid get_index_constraint(Oid fc_indexId)
{
	Oid			fc_constraintId = InvalidOid;
	Relation	fc_depRel;
	ScanKeyData fc_key[3];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	/* 在依赖表中搜索索引 */
	fc_depRel = table_open(DependRelationId, AccessShareLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationRelationId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_indexId));
	ScanKeyInit(&fc_key[2],
				Anum_pg_depend_objsubid,
				BTEqualStrategyNumber, F_INT4EQ,
				Int32GetDatum(0));

	fc_scan = systable_beginscan(fc_depRel, DependDependerIndexId, true,
							  NULL, 3, fc_key);

	while (HeapTupleIsValid(fc_tup = systable_getnext(fc_scan)))
	{
		Form_pg_depend fc_deprec = (Form_pg_depend) GETSTRUCT(fc_tup);

		/*
		 * 我们假设对约束的任何内部依赖必须是我们
		 * 要寻找的内容。
		 */
		if (fc_deprec->refclassid == ConstraintRelationId &&
			fc_deprec->refobjsubid == 0 &&
			fc_deprec->deptype == DEPENDENCY_INTERNAL)
		{
			fc_constraintId = fc_deprec->refobjid;
			break;
		}
	}

	systable_endscan(fc_scan);
	table_close(fc_depRel, AccessShareLock);

	return fc_constraintId;
}

/*
 * get_index_ref_constraints
 *		给定索引的OID，返回引用该索引的所有外键约束的OID。
 */
List * get_index_ref_constraints(Oid fc_indexId)
{
	List	   *fc_result = NIL;
	Relation	fc_depRel;
	ScanKeyData fc_key[3];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	/* 在依赖表中搜索索引 */
	fc_depRel = table_open(DependRelationId, AccessShareLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_depend_refclassid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationRelationId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_depend_refobjid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_indexId));
	ScanKeyInit(&fc_key[2],
				Anum_pg_depend_refobjsubid,
				BTEqualStrategyNumber, F_INT4EQ,
				Int32GetDatum(0));

	fc_scan = systable_beginscan(fc_depRel, DependReferenceIndexId, true,
							  NULL, 3, fc_key);

	while (HeapTupleIsValid(fc_tup = systable_getnext(fc_scan)))
	{
		Form_pg_depend fc_deprec = (Form_pg_depend) GETSTRUCT(fc_tup);

		/*
		 * 我们假设对约束的任何正常依赖必须是我们
		 * 要寻找的内容。
		 */
		if (fc_deprec->classid == ConstraintRelationId &&
			fc_deprec->objsubid == 0 &&
			fc_deprec->deptype == DEPENDENCY_NORMAL)
		{
			fc_result = lappend_oid(fc_result, fc_deprec->objid);
		}
	}

	systable_endscan(fc_scan);
	table_close(fc_depRel, AccessShareLock);

	return fc_result;
}
